package com.zkh.myframe.database.mapper.proxy;

import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.zkh.myframe.core.FrameUtils;
import com.zkh.myframe.database.DataTemplate;
import com.zkh.myframe.database.mapper.MapperEntity;
import com.zkh.myframe.database.mapper.MapperException;
import com.zkh.myframe.database.mapper.MapperPageRequest;
import com.zkh.myframe.database.mapper.MapperPageResponse;
import com.zkh.myframe.database.mapper.SQLEntity;
import com.zkh.myutils.bean.BeanUtils;
import com.zkh.myutils.database.conutils.PoolUtils;
import com.zkh.myutils.database.data.handler.ArrayListHandler;
import com.zkh.myutils.database.data.handler.BeanListHandler;
import com.zkh.myutils.database.data.handler.ColumnListHandler;
import com.zkh.myutils.database.data.handler.MapListHandler;
import com.zkh.myutils.database.dbpool.ConnectConfigure;
import com.zkh.myutils.database.dbpool.DBType;
import com.zkh.myutils.page.PageUtil;
import com.zkh.myutils.proxy.MethodHandler;
import com.zkh.myutils.proxy.ProxyMethod;
import com.zkh.myutils.utils.Regexp;
import com.zkh.myutils.utils.StringUtils;

/**
 * Mapper代理对象
 * @author zkh
 */
public class MapperProxy implements MethodHandler {
	
	/**
	 * 单字段查询
	 */
	private static final List<String> singleTypeList = Arrays.asList(
		BigDecimal.class.getName(), BigInteger.class.getName(),
		Long.class.getName(), Integer.class.getName(), Short.class.getName(), Byte.class.getName(), Boolean.class.getName(),
		Float.class.getName(), Double.class.getName(), String.class.getName(), Date.class.getName()
	);
	
	@Override
	public Object invoke(Object proxy, ProxyMethod proxyMethod) throws Throwable {
		//参数容器
		Map<String, Object> paramMap = new HashMap<>();
		//分页对象，不为null表示分页
		MapperPageRequest[] mapperPageRequest = new MapperPageRequest[1];
		//遍历参数
		proxyMethod.forEachParam(paramReader->{
			//未分页，则检查是否分页
			if(mapperPageRequest[0]==null) {
				//是否实现MapperPageRequest接口
				boolean selectByPage = MapperPageRequest.class.isAssignableFrom(paramReader.getType());
				//分页，但是为空
				if(selectByPage) {
					if(paramReader.getParamValue()==null) {
						throw new MapperException("分页参数对象"+paramReader.getType().getName()+"不能为null");
					}else {
						mapperPageRequest[0] = (MapperPageRequest) paramReader.getParamValue();
					}
				}
			}
			//实例化参数对象（默认名+参数原名）
			paramMap.put("arg" + paramReader.getIndex(), paramReader.getParamValue());
			paramMap.put(paramReader.getParamName(), paramReader.getParamValue());
		});
		/*******  执行并返回数据 *******/
		return execute(proxyMethod.getSuperClassName(), proxyMethod.getSuperName(), proxyMethod.getMethod().getGenericReturnType(), paramMap, mapperPageRequest[0]);
	}
	
	/**
	 * 执行并返回数据
	 * @param namespace 命名空间
	 * @param id ID
	 * @param rtnType 返回值类型(无返回值为void)
	 * @return
	 */
	private Object execute(String namespace, String id, Type rtnType, Map<String, Object> paramMap, MapperPageRequest mapperPageRequest) {
		//返回值
		Object rtnValue = null;
		//获取Mapper实例
		SQLEntity sqlEntity = MapperEntity.getOperateSQLEntity(namespace, id);
		//为空
		if(sqlEntity==null) {
			throw new MapperException("未配置" + namespace + "." + id + "方法的Mapper映射");
		}
		//获取SQL
		String sql = sqlEntity.getSql(paramMap);
		//查询类型
		String sqltype = sqlEntity.getOperType();
		//数据源
		String dataSource = sqlEntity.getDataSource();
		//参数
		Object[] params = sqlEntity.getParams();
		//DataTemplate对象
		DataTemplate template = FrameUtils.getResourceClass(DataTemplate.class);
		//有指定数据源
		if(StringUtils.isNotEmpty(dataSource)) {
			template.setDataSource(dataSource);
		}
		//查询
		if("select".equals(sqltype)) {
			rtnValue = query(template, sql, params, rtnType, mapperPageRequest);
		}else {//增删改
			//返回类型
			String type = rtnType.getTypeName();
			//执行
			int res = template.doExecute(PoolUtils.getTransactionConnection(), sql, params);
			//返回值
			if("int".equals(type) || "java.lang.Integer".equals(type)) {
				rtnValue = res;
			}else if("boolean".equals(type) || "java.lang.Boolean".equals(type)) {
				return true;
			}else if("void".equals(type)){
				rtnValue = null;
			} else {
				throw new MapperException(sqltype.toUpperCase() + "操作只能返回整型和boolean类型");
			}
		}
		//返回值
		return rtnValue;
	}
	
	/**
	 * 执行Select语句并返回对应数据
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Object query(DataTemplate template, String sql, Object[] params, Type rtnType, MapperPageRequest mapperPageRequest) {
		try {
			//是否分页
			if(mapperPageRequest!=null) {
				//获取返回类型
				String rtnTypeName = rtnType.getTypeName();
				//获取两个类型
				//返回类型
				if(!rtnTypeName.matches("com.zkh.(myframe.database.mapper.MapperPageResponse|myutils.page.Pagination)<.+>")) {
					throw new MapperException("分页查询必须返回MapperPageResponse或Pagination类");
				}
				//获取泛型类型
				String type = rtnTypeName.substring(rtnTypeName.indexOf('<')+1, rtnTypeName.length()-1).replaceAll("<.+>", "");
				//泛型类型为数组
				boolean isArray = "java.lang.Object[]".equals(type);
				//页码信息
				Integer pageIndex = mapperPageRequest.getPageIndex(), pageItem = mapperPageRequest.getPageItem();
				//每页条数限制
				pageItem = pageItem==null || pageItem<=0 ? 10 : (pageItem>1000 ? 1000: pageItem);
				//添加分页
				template.addPage(pageIndex==null || pageIndex<=0?1:pageIndex, pageItem);
				//分页查询
				PageUtil page = isArray ? template.doSelectPage(Object[].class, sql, params) : template.doSelectPage(Map.class, sql, params);
				//返回对象
				MapperPageResponse response = new MapperPageResponse();
				//分页信息
				response.setTotalItem(page.getTotalItem());
				response.setTotalPage(page.getTotalPage());
				response.setPageIndex(page.getPageIndex());
				response.setPageItem(page.getPageItem());
				//Map和数组
				if("java.util.Map".equals(type) || isArray) {
					//直接保存
					response.setDataList(page.getDataList());
				}else {
					//初始化
					response.setDataList(new ArrayList());
					//实例化实体类
					Class<?> instance = Class.forName(type);
					//直接转换
					for(Object o: page.getDataList()) {
						response.getDataList().add(BeanUtils.mapToBean((Map) o, instance));
					}
				}
				//返回对象
				return response;
			}else {
				//类型
				String type = rtnType.getTypeName();
				//多记录查询
				boolean multiQuery = type.startsWith("java.util.List");
				//单记录查询
				if(!multiQuery) {
					//库类型
					DBType dbtype = ConnectConfigure.getDefaultSourceConfig().getDbtype();
					//MySQL
					if(DBType.TYPE_MYSQL.equals(dbtype)) {
						sql += " limit 1";
					}else if(DBType.TYPE_ORACLE.equals(dbtype)) {
						sql = "select * from (" + sql + ") t where rownum=1";
					}else if(DBType.TYPE_SQLSERVER.equals(dbtype)) {
						sql = "select top 1" + sql.substring(6);
					}
				}else {
					//获取泛型类型
					type = Regexp.getMatchOne("(?<=java.util.List<).+(?=>)", rtnType.getTypeName()).replaceAll("<.+>", "");
				}
				//是否查数组
				boolean isArray = "java.lang.Object[]".equals(type);
				//执行查询
				if(isArray) {
					//查询结果
					List<Object[]> list = template.doQuery(PoolUtils.getTransactionConnection(), sql, new ArrayListHandler(), params);
					//返回
					return multiQuery ? list : (list.size()==0 ? null : list.get(0));
				}
				//如果是Map
				if(type.startsWith("java.util.Map")) {
					//查询结果
					List<Map<String, Object>> list = template.doQuery(PoolUtils.getTransactionConnection(), sql, new MapListHandler(), params);
					//返回
					return multiQuery ? list : (list.size()==0 ? null : list.get(0));
				}
				//单字段查询
				if(singleTypeList.contains(type)) {
					//查询数据
					List<?> singleList = template.doQuery(PoolUtils.getTransactionConnection(), sql, new ColumnListHandler<Object>(Class.forName(type)), params);
					//返回
					return multiQuery ? singleList : (singleList.size()==0 ? null : singleList.get(0));
				}
				//查询结果
				List<?> list = template.doQuery(PoolUtils.getTransactionConnection(), sql, new BeanListHandler<>(Class.forName(type)), params);
				//返回
				return multiQuery ? list : (list.size()==0 ? null : list.get(0));
			}
		}catch(ClassNotFoundException e) {
			throw new MapperException("结果集转换失败", e);
		}
	}
}
